package org.computate.controller.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.computate.model.dao.ComputateDao;
import org.computate.model.dao.UploadDao;
import org.computate.model.item.Model;
import org.computate.model.item.Upload;
import org.computate.tool.ModelTool;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.inject.Inject;
import com.google.inject.Singleton;


@Singleton
public class ComputateItemFilter implements Filter {
	
	@Inject private ModelTool modelTool;
	@Inject private UploadDao uploadDao;
	
	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest)servletRequest;
		HttpServletResponse response = (HttpServletResponse)servletResponse;
		String requestURI = request.getRequestURI();
		
		try {
			if(StringUtils.endsWith(requestURI, "Index.jsp")) {
				String modelName = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(requestURI, "Index.jsp"), "/");
				List<? extends Model> items = index(request, response, modelName);
				
				filterIndex(request, response, items);
			}
			else if(StringUtils.endsWith(requestURI, "Add.jsp")) {
				String modelName = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(requestURI, "Add.jsp"), "/");
				String nameParam = request.getParameter("name");
				Model item = add(request, response, modelName, nameParam);
				
				filterAdd(request, response, item);
			}
			else if(StringUtils.endsWith(requestURI, "View.jsp")) {
				String modelName = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(requestURI, "View.jsp"), "/");
				Model item = view(request, response, modelName);
				
				filterView(request, response, item);
			}
			else if(StringUtils.endsWith(requestURI, "Delete.jsp")) {
				String modelName = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(requestURI, "Delete.jsp"), "/");
				Model item = delete(request, response, modelName);
				
				filterDelete(request, response, item);
			}
			else if(StringUtils.endsWith(requestURI, "UploadPhoto.jsp")) {
				String modelName = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(requestURI, "UploadPhoto.jsp"), "/");
				Model item = uploadPhoto(request, response, modelName, uploadDao);
				
				filterUploadPhoto(request, response, item);
			}
			else if(StringUtils.endsWith(requestURI, "Edit.jsp")) {
				String modelName = StringUtils.substringAfterLast(StringUtils.substringBeforeLast(requestURI, "Edit.jsp"), "/");
				Model item = edit(request, response, modelName);
				
				filterEdit(request, response, item);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		filterChain.doFilter(servletRequest, servletResponse);
	}
	
	public List<? extends Model> index(HttpServletRequest request, HttpServletResponse response, String modelName) throws InstantiationException, IllegalAccessException {
		Class<?> daoClass = modelTool.getDaoClass(modelName);
		Class<?> itemClass = modelTool.getItemClass(modelName);
		List<? extends Model> items = new ArrayList<Model>();
		
		if(daoClass != null && itemClass != null) {
			ComputateDao<?> dao = modelTool.getDao(modelName);
			Model newInstance = (Model)itemClass.newInstance();
			
			items = dao.list(newInstance.getListLevels());
			request.setAttribute("modelName", modelName);
			request.setAttribute("modelDisplayName", newInstance.getModelDisplayName());
			request.setAttribute("modelPluralName", newInstance.getModelPluralName());
			request.setAttribute("addLink", newInstance.getAddLink());
			request.setAttribute("addLinkName", newInstance.getAddLinkName());
			request.setAttribute("items", items);
		}
		
		return items;
	}
	
	public Model add(HttpServletRequest request, HttpServletResponse response, String modelName, String name) throws InstantiationException, IllegalAccessException {
		return modelTool.add(request, response, modelName, name);
	}
	
	public Model view(HttpServletRequest request, HttpServletResponse response, String modelName) throws InstantiationException, IllegalAccessException {
		Class<?> itemClass = modelTool.getItemClass(modelName);
		ComputateDao<?> dao = modelTool.getDao(modelName);
		String idParam = request.getParameter("id");
		Long id = NumberUtils.toLong(idParam);
		Model newInstance = (Model)itemClass.newInstance();

		Model item = dao.get(id, newInstance.getGetLevels());
		
		request.setAttribute("modelName", modelName);
		request.setAttribute("modelDisplayName", newInstance.getModelDisplayName());
		request.setAttribute("modelPluralName", newInstance.getModelPluralName());
		request.setAttribute("item", item);
		
		return item;
	}
	
	public Model delete(HttpServletRequest request, HttpServletResponse response, String modelName) throws InstantiationException, IllegalAccessException {
		ComputateDao<?> dao = modelTool.getDao(modelName);
		String idParam = request.getParameter("id");
		Long id = NumberUtils.toLong(idParam);
		Model item = dao.delete(id);
		
		request.setAttribute("modelName", modelName);
		request.setAttribute("item", item);
		
		return item;
	}
	
	public Model uploadPhoto(HttpServletRequest request, HttpServletResponse response, String modelName, UploadDao uploadDao) throws InstantiationException, IllegalAccessException {
		ComputateDao<?> dao = modelTool.getDao(modelName);
		Class<?> itemClass = modelTool.getItemClass(modelName);
		String idParam = null;
		Long id = null;
		Model item = null;
		Model newInstance = (Model)itemClass.newInstance();
		
		
		if (ServletFileUpload.isMultipartContent(request)) {
			try {
				ServletFileUpload upload = new ServletFileUpload();
				FileItemIterator iter = upload.getItemIterator(request);
				FileItemStream fileItemStream = iter.next();

				while(fileItemStream != null) {
					String contentType = fileItemStream.getContentType();
					
					if (fileItemStream.isFormField()) {
						String fieldName = fileItemStream.getFieldName();
						
						if (fieldName.equals("itemId")) {
							idParam = IOUtils.toString(fileItemStream.openStream());
							id = Long.parseLong(idParam);
							item = dao.get(id, newInstance.getGetLevels());
						}
					} 
					else if (contentType != null)
					{
						byte[] bytes = IOUtils.toByteArray(fileItemStream.openStream());
						int K = 524288;
						
						Upload first = null;
						Upload previous = null;
						for(int i = 0; i < bytes.length; i += K) {
							// construct our entity objects
							int end = (i + K) < bytes.length ? i + K : bytes.length;
							Blob imageBlob = new Blob(Arrays.copyOfRange(bytes, i, end));
							Upload image = new Upload();
							String newContentType = "image/jpeg";
							String filename = fileItemStream.getName();
							
							image.setName(fileItemStream.getName());
							if(filename.endsWith(".gif"))
								newContentType = "image/gif";
							if(filename.endsWith(".png"))
								newContentType = "image/png";
							if(filename.endsWith(".tif"))
								newContentType = "image/tiff";
							if(filename.endsWith(".tiff"))
								newContentType = "image/tiff";
							if(filename.endsWith(".bmp"))
								newContentType = "image/x-ms-bmp";

							image.setItemName(modelName);
							image.setItemId(id);
							image.setImage(imageBlob);
							image.setContentType(newContentType);
							// persist image
							uploadDao.put(image);
							if (previous == null) {
								first = image;
							} else {
								previous.setNextId(image.getId());
								uploadDao.put(previous);
							}
							previous = image;
						}
						if (first != null) {
							Queue queue = QueueFactory.getDefaultQueue();
							queue.add(TaskOptions.Builder.withUrl(
									"/page/item/Upload/UploadProcessPhoto.jsp")
									.param("id", first.getId().toString()));
						}
					}
					fileItemStream = iter.next();
				}
			} catch (NoSuchElementException e) {
				// don't do anything, it's okay if this is thrown when the iter.next() method is called.
			} catch (NumberFormatException e) {
				e.printStackTrace();
			} catch (FileUploadException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		request.setAttribute("modelName", modelName);
		request.setAttribute("item", item);
		
		return item;
	}
	
	public Model edit(HttpServletRequest request, HttpServletResponse response, String modelName) throws InstantiationException, IllegalAccessException {
		Class<?> itemClass = modelTool.getItemClass(modelName);
		ComputateDao<?> dao = modelTool.getDao(modelName);
		String idParam = request.getParameter("id");
		String fieldParam = request.getParameter("field");
		String valueParam = request.getParameter("value");
		String uriParam = request.getParameter("uri");
		Long id = NumberUtils.toLong(idParam);
		Model newInstance = (Model)itemClass.newInstance();

		Model item = dao.get(id, newInstance.getGetLevels());
		
		if(item != null) {
			modelTool.edit(fieldParam, itemClass, item, valueParam, dao);
		}
		
		try {
			response.sendRedirect(uriParam);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return item;
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
	}

	protected void filterIndex(HttpServletRequest request, HttpServletResponse response, List<? extends Model> itemObjects) {
		
	}
	
	protected void filterAdd(HttpServletRequest request, HttpServletResponse response, Model itemObject) {
		
	}
	
	protected void filterView(HttpServletRequest request, HttpServletResponse response, Model itemObject) {
		
	}
	
	protected void filterDelete(HttpServletRequest request, HttpServletResponse response, Model itemObject) {
		
	}
	
	protected void filterUploadPhoto(HttpServletRequest request, HttpServletResponse response, Model itemObject) {
		
	}
	
	protected void filterEdit(HttpServletRequest request, HttpServletResponse response, Model itemObject) {
		
	}
}
